package han.cirno.corrupt.data.instances

import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import han.cirno.cirno_dao.CirnoColQuery
import han.cirno.cirno_dao.CirnoDaoAwA
import han.cirno.cirno_dao.CirnoSingleQuery
import han.cirno.corrupt.data.DB
import han.cirno.corrupt.components.calendar.CalendarUtil
import java.util.Calendar
import java.util.Date
import kotlin.random.Random

/**
 * 快进到我再也不搞重构了
 * ——数据库重构开始 2025/04/05
 * @author CCirno
 */
object UserClassesData {
    var lastUpdateTime:Date?=null
    private var classTables:MutableList<ClassTable> =ArrayList()
    private var classTableDirty:Boolean=true
    //TODO add network date info fetch
    private var dateInfos:MutableList<DateInfo> =ArrayList<DateInfo>().also {
        it.add(
            (
                    DateInfo(
                        "2024-2025","2",
                        CalendarUtil.findMondayOfWeekInYearTimeInMillis(2025,2,17)
                    ))
        )
    }
    @CirnoDaoAwA
    @Entity
    data class ClassTable(
        val year:String,
        val term:String,
        @Ignore val classEntries:MutableList<ClassEntry>,
        @PrimaryKey var uid:Long=Random.nextLong()
    ) {
        constructor(year:String,term:String):this(year,term,ArrayList())
    }
    @CirnoDaoAwA
    @Entity
    data class DateInfo(
        val year:String,
        val term:String,
        val firstDay:Long,
        @PrimaryKey val uid:Long=Random.nextLong()
    )
    @CirnoDaoAwA
    @Entity
    data class ClassEntry(
        @CirnoSingleQuery @CirnoColQuery val yearAndTerm:String,
        var name:String,
        var teachers:String,
        var dow:DayOfWeek,
        var weeks:IntArray,
        var time:TimeRange,
        var place:String,
        var user:Boolean=false,
        @PrimaryKey var uid:Long=Random.nextLong()
    ) {
        override fun equals(other:Any?):Boolean {
            if(this===other) return true
            if(javaClass!=other?.javaClass) return false

            other as ClassEntry

            if(name!=other.name) return false
            if(teachers!=other.teachers) return false
            if(dow!=other.dow) return false
            if(!weeks.contentEquals(other.weeks)) return false
            if(time!=other.time) return false
            if(place!=other.place) return false
            if(user!=other.user) return false

            return true
        }

        override fun hashCode():Int {
            var result=name.hashCode()
            result=31*result+teachers.hashCode()
            result=31*result+dow.hashCode()
            result=31*result+weeks.contentHashCode()
            result=31*result+time.hashCode()
            result=31*result+place.hashCode()
            result=31*result+user.hashCode()
            return result
        }
    }

    enum class DayOfWeek(val serial:Int) {
        Mon(0), Tue(1), Wed(2), Thur(3), Fri(4), Sat(5), Sun(6);

        companion object {
            fun valueOf(serial:Int):DayOfWeek? {
                return entries.find {it.serial==serial}
            }

            fun valueOfJavaCalendarConst(dayOfWeek:Int):DayOfWeek? {
                return when(dayOfWeek) {
                    Calendar.SUNDAY->Sun
                    Calendar.MONDAY->Mon
                    Calendar.TUESDAY->Tue
                    Calendar.WEDNESDAY->Wed
                    Calendar.THURSDAY->Thur
                    Calendar.FRIDAY->Fri
                    Calendar.SATURDAY->Sat
                    else->null
                }
            }
        }
    }

    data class TimeRange(val from:Int,val to:Int)
    @Suppress("USELESS_ELVIS")
    private fun updateClassTables() {
        val classTableDao=DB.getInstance().classTableDao()
        val classEntryDao=DB.getInstance().classEntryDao()
        val table=classTableDao.getFirst() ?: return
        val year=table.year
        val term=table.term
        val classEntries=classEntryDao.getListByYearAndTerm("$year:$term").toMutableList() ?: return
        classTables.clear()
        classTables.add(ClassTable(year,term,classEntries))
    }

    fun tryFetchClassTable():ClassTable? {
        //TODO too ugly!!
        if(classTableDirty) updateClassTables()
        return if(classTables.size!=0) classTables[0] else null
    }

    fun tryFetchFirstDay():Long {
        val classTable=tryFetchClassTable() ?: return 0
        val dateInfo=dateInfos.find {it.year==classTable.year&&it.term==classTable.term} ?: return 0
        return dateInfo.firstDay
    }

    fun makeDirty() {
        classTableDirty=true
    }
}